#define _CRT_SECURE_NO_WARNINGS 1

//#include <iostream>
//#include <ctime>
//using namespace std;
//
//struct A { int a[10000]; };
//
//void TestFunc1(A a) {}
//void TestFunc2(A& a) {} // 效率高不少
//
//void TestReturnByRefOrValue()
//{
//	A a;
//	// 以值作为函数的返回值类型
//	size_t begin1 = clock();
//	for (size_t i = 0; i < 100000; ++i)
//		TestFunc1(a);
//	size_t end1 = clock();
//	// 以引用作为函数的返回值类型
//	size_t begin2 = clock();
//	for (size_t i = 0; i < 100000; ++i)
//		TestFunc2(a);
//	size_t end2 = clock();
//	// 计算两个函数运算完成之后的时间
//	cout << "TestFunc1 time:" << end1 - begin1 << endl;
//	cout << "TestFunc2 time:" << end2 - begin2 << endl;
//}
//
//int main()
//{
//	TestReturnByRefOrValue();
//
//	return 0;
//}

//#include <iostream>
//using namespace std;
//
//int main()
//{
//	// 权限可以平移/缩小，不能放大
//	double d = 12.34;
//
//	// 类型转换，会产生中间变量
//	int i = d; 
//
//	// d 给 r 会产生临时变量
//	// 临时变量具有常性，可以理解为相当于被 const 修饰了
//	// 相当于权限放大
//	// int& r = d; // err
//	const int& r = d; // right，but why?
//
//	int x = 0, y = 1;
//	// int& r = x + y; // err
//	const int& r = x + y; // right
//	return 0;
//}

//#include <iostream>
//using namespace std;
//
//int main()
//{
//	int* ptr = NULL;
//
//	// 语法上虽然看起来有解引用
//	// 但是底层是开创一个指针把ptr存起来
//	int& r = *ptr; // right
//
//	int a = 0;
//	int& b = a;
//
//	cout << sizeof(b) << endl;
//	cout << sizeof(a) << endl;
//
//	// 当输出r的时候，就发生解引用了！
//	// cout << r << endl; // err
//
//	return 0;
//}


// 引用和指针的不同点:
// 1. 引用概念上定义一个变量的别名，指针存储一个变量地址。
// 2. 引用在定义时必须初始化，指针没有要求
// 3. 引用在初始化时引用一个实体后，就不能再引用其他实体
// 而指针可以在任何时候指向任何一个同类型实体
// 4. 没有NULL引用，但有NULL指针
// 5. 在sizeof中含义不同：引用结果为引用类型的大小
// 但指针始终是地址空间所占字节个数(32位平台下占4个字节)
// 6. 引用自加即引用的实体增加1，指针自加即指针向后偏移一个类型的大小
// 7. 有多级指针，没有多级引用
// 8. 访问实体方式不同，指针需要显式解引用，引用编译器自己处理
// 9. 引用比指针使用起来相对更安全

// 语法上引用不开空间，但是在底层来说还是开的
// 上层跟下层有可能会截然不同
// C++ 的引用是不能完全替代指针的
// 你想想链表，就必须用指针，因为必须要改变指向

// Java中，只有引用，只是跟C++有本质的区别，可以改变指针




// 内联函数

// 假设有一个频繁调用的小函数swap，大量调用会建立栈帧，消耗内存
// C本质上是用宏来解决，这很麻烦
// 比如最简单的下面这个
// #define Add(x, y) ((x) + (y))

//#include <iostream>
//using namespace std;
//
//// inline是一种以空间换时间的做法
//// 如果编译器将函数当成内联函数处理，在编译阶段，会用函数体替换函数调用
//// 缺陷：可能会使目标文件变大，优势：少了调用开销，提高程序运行效率。
//
//// inline对于编译器而言只是一个建议，不同编译器关于inline实现机制可能不同，
//// 一般建议：将函数规模较小(即函数不是很长，具体没有准确的说法，取决于编译器内部实现)
//// 不是递归、且频繁调用的函数采用inline修饰，否则编译器会忽略inline特性。
//
////  inline不建议声明和定义分离，分离会导致链接错误。
//// 因为inline被展开，就没有函数地址了，链接就会找不到。
//
//// Debug版本下面默认是不展开的
//// 方便调试
//
//inline int Add(int a, int b)
//{
//	return a + b;
//}
//
//int main()
//{
//	// 假如不加inline，调用会消耗栈帧
//	// 加了的话，就是直接展开
//	int c = Add(1, 2); 
//	cout << c << endl;
//
//	return 0;
//}
//
////  C++有哪些技术替代宏？
//// 1. 常量定义 换用const enum
//// 2. 短小函数定义 换用内联函数





// auto关键字 (C11)

// 1.替代长类型
//#include <iostream>
//using namespace std;
//
//int TestAuto()
//{
//	return 10;
//}
//int main()
//{
//	int a = 10;
//	auto b = a;
//	auto c = 'a';
//	auto d = TestAuto();
//
//	cout << typeid(b).name() << endl;
//	cout << typeid(c).name() << endl;
//	cout << typeid(d).name() << endl;
//	//auto e; 无法通过编译，使用auto定义变量时必须对其进行初始化
//
//	return 0;
//}

// 使用auto定义变量时必须对其进行初始化，
// 在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。
// 因此auto并非是一种“类型”的声明，而是一个类型声明时的“占位符”，
// 编译器在编译期会将auto替换为变量实际的类型。

// 用auto声明指针类型时，用auto和auto*没有任何区别，但用auto声明引用类型时则必须加 &
// void TestAuto()
//{
//	auto a = 1, b = 2;
//	auto c = 3, d = 4.0;  // 该行代码会编译失败，因为c和d的初始化表达式类型不同
//}

// auto不能作为函数的参数
// auto不能直接用来声明数组

// auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环
// 还有lambda表达式等进行配合使用。


//// 基于范围的for循环(C++11)
//// 自动取数组array中，赋值给e
//// 自动++，自动判断结束
//// 抄Python的作业
//// 写死，从头到尾 
// 
// 
//#include <iostream>
//using namespace std;
//
//void TestFor()
//{
//	int array[] = { 1, 2, 3, 4, 5 };
//	for (auto& e : array) // 如果想修改的话，那就要用引用
//		e *= 2;
//	for (auto e : array)
//		cout << e << " ";
//
//}
//
//int main()
//{
//	TestFor();
//
//	return 0;
//}

//void TestFor(int array[])
//{
//	for (auto& e : array)
//		cout << e << endl;
//}
// 
// 效率指的是执行效率，不是开发效率
// C/C++ 极度追求效率，先编译再运行
// Python相对，是极度不追求效率的语言，边编译再运行

// nullptr (C11)
// 前人挖的坑，C++中NULL就是0
//#include <iostream>
//using namespace std;
//
//void f(int)
//{
//	cout << "f(int)" << endl;
//}
//void f(int*)
//{
//	cout << "f(int*)" << endl;
//}
//int main()
//{
//	f(0);
//	f(NULL); // 我们想的是匹配第二个，结果是第一个，这就是错误的宏替换带来的后果
//	f((int*)NULL);
//	return 0;
//}

// 为了填这个坑，于是C++中就有了nullptr
// 不需要头文件，作为新关键字引入
// 以后所有指针的空值统一直接用nullptr

// 小语法，本节最重要的就是函数重载和引用
